package com.ydd.JinJavaPractice.poiTL;

import com.alibaba.fastjson.JSONObject;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.data.*;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;


/**
 * Author: yangdongdong  <br>
 * Date: 2024/12/24 下午4:50  <br>
 * Version: 1.0  <br>
 * Description: 利用poi-tl去渲染docx模板。<br>
 * <strong> doc文件不可以，因为是2进制文件 </strong>
 */

public class PoiTL
{
    //创建目标文件
    private static final String SOURCE_DIR = "C:\\Project\\JinJavaPractice\\src\\main\\resources\\";
    private static final String DEST_DIR = "C:\\Project\\JinJavaPractice\\src\\main\\resources\\";


    /**
     * 简单的key-value渲染
     * @throws IOException IO异常
     */
    public static void simpleRender() throws IOException
    {
        //===================使用Map的方式================================
        //创建目标文件
        String sourceFile = "C:\\Project\\JinJavaPractice\\src\\main\\resources\\SimpleTemplate.docx";
        String destPath = "C:\\Project\\JinJavaPractice\\src\\main\\resources\\SimpleTemplate-output.docx";
        //构建数据
        Map<String, Object> data = new HashMap<>();
        data.put("name", "yangdongdong");
        data.put("age", "999");
        data.put("address", "黄土高原");
        //创建输出流
        OutputStream os = Files.newOutputStream(Paths.get(destPath));
        //最终编译渲染并输出
        try(XWPFTemplate xwpfTemplate = XWPFTemplate.compile(sourceFile))
        {
            xwpfTemplate.render(data).writeAndClose(os);
        }
    }



    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class Student
    {
        //姓名
        private String name;

        //年龄
        private String sid;

        //地址
        private String birth;
    }

    /**
     * 表格行循环测试
     * @throws IOException IO异常
     */
    public static void rowLoopRender() throws IOException
    {
        //创建行循环策略
        LoopRowTableRenderPolicy rowTableRenderPolicy = new LoopRowTableRenderPolicy();
        //告诉模板引擎，要在students做行循环,绑定行循环策略
        Configure configure = Configure.builder().bind("students", rowTableRenderPolicy).build();


        //构建数据
        Map<String, Object> data = new HashMap<>();

        //1.学生数据
        List<Student> students = new ArrayList<>();
        Student s1 = new Student("小明", "001", "2022-09-01");
        Student s2 = new Student("小红", "002", "2022-09-02");
        students.add(s1);
        students.add(s2);
        //2.设置到students字段中
        data.put("students", students);

        //创建输出流
        OutputStream os = Files.newOutputStream(Paths.get(DEST_DIR + "RowLoop-output.docx"));
        //最终编译渲染并输出
        String sourceFile = SOURCE_DIR + "RowLoop.docx";
        try(XWPFTemplate xwpfTemplate = XWPFTemplate.compile(sourceFile))
        {
            xwpfTemplate.render(data).writeAndClose(os);
        }


    }


    /**
     * 文本测试<br>
     * 推荐使用工厂 Texts 构建文本模型。<br>
     * {
     *   "text": "Sayi",
     *   "style": {
     *     "strike": false,  删除线
     *     "bold": true, 粗体
     *     "italic": false,  斜体
     *     "color": "00FF00", 颜色，不能加井号 “#”
     *     "underLine": false,  下划线
     *     "fontFamily": "微软雅黑", 字体
     *     "fontSize": 12, 字号
     *     "highlightColor": "green", 	背景高亮色
     *     "vertAlign": "superscript",  上标或下标
     *     "characterSpacing" : 20    字间距
     *   }
     * }
     * @throws IOException IO异常
     */

    public static void textTest () throws IOException
    {
        HashMap< String,  Object> data =  new HashMap<>();
        data.put("name", "Sayi");
        data.put("author", Texts.of("这里是红色").color("ed0909").create());
        data.put("link", Texts.of("website").link("http://deepoove.com").create());
        data.put("anchor", Texts.of("anchortxt").anchor("appendix1").create());


        myRender(SOURCE_DIR+"Text.docx",data,DEST_DIR+"text-output.docx");
    }


    /**
     * 图片渲染测试
     * @throws IOException IO异常
     * <p>
     * 图片标签以@开始：{{@var}}
     */
    public static void pictureTest () throws IOException
    {
        HashMap< String,  Object> data =  new HashMap<>();
        // 指定图片路径
        data.put("image", "C:\\Users\\Administrator\\Desktop\\表情包\\20240903-102313.jpg");
        // svg图片
        data.put("svg", "https://img.shields.io/badge/jdk-1.6%2B-orange.svg");

        // 图片文件
        data.put("imageWithSize", Pictures.ofLocal("C:\\Users\\Administrator\\Desktop\\表情包\\20240903-102313.jpg").size(120, 120).create());

        // 图片流
        data.put("streamImg", Pictures.ofStream(Files.newInputStream(Paths.get("C:\\Users\\Administrator\\Desktop\\表情包\\20240903-102313.jpg")), PictureType.JPEG)
                .size(100, 120).create());

        // 网络图片(注意网络耗时对系统可能的性能影响)
        data.put("urlImg", Pictures.ofUrl("http://deepoove.com/images/icecream.png")
                .size(100, 100).create());

        // java图片，我们可以利用Java生成图表插入到word文档中
//        data.put("buffered", Pictures.ofBufferedImage(bufferImage, PictureType.PNG)
//                .size(100, 100).create());


        myRender(SOURCE_DIR+"Picture.docx",data,DEST_DIR+"Picture-output.docx");
    }


    /**
     * 列表渲染测试
     * @throws IOException IO异常
     * <p>
     * 列表标签以*开始：{{*var}}
     */
    public static void listTest () throws IOException
    {
        HashMap< String,  Object> data =  new HashMap<>();
        data.put("myList", Numberings.of(NumberingFormat.DECIMAL)
                .addItem("Plug-in grammar")
                .addItem("Supports word text, pictures, table...")
                .addItem("Not just templates")
                .create()
                );

        myRender(SOURCE_DIR+"List.docx",data,DEST_DIR+"List-output.docx");

    }



    /**
     * 列表渲染测试
     * @throws IOException IO异常
     * <p>
     * 列表标签以*开始：{{*var}}
     */
    public static void sectionTest () throws IOException
    {
        //1. 一次循环：当value是一个对象时，比如nested对象
        JSONObject nested = new JSONObject();
        nested.put("name", "小明");
        nested.put("sid", "001");
        nested.put("birth", "2022-09-01");
        JSONObject mySection = new JSONObject();
        mySection.put("mySection", nested);



        //2. 多次循环
        List<JSONObject> mySectionList = new ArrayList<>();
        for(int i = 0; i < 3; i++)
        {
            JSONObject nestedObject = new JSONObject();

            nestedObject.put("name", "小明"+i);
            nestedObject.put("sid", "00"+i);
            nestedObject.put("birth", "2022-09-0"+i);

            mySectionList.add(nestedObject);
        }

        mySection.put("mySectionList", mySectionList);

        //mySection里有两个属性，一个是mySection单对象，一个是mySectionList对象列表
        myRender(SOURCE_DIR+"Section.docx",mySection,DEST_DIR+"Section-output.docx");
    }

    /**
     * 引用图片测试：用于替换模板中已有的图片<br>
     * 1. 在模板中选中要替换的图片，然后右键，选择倒数第三项“编辑替换文字”，在输入区域填写标签来引用该图片，比如{{img}}<br>
     * <p>
     * 引用图片标签只会替换图片而不会改变图片尺寸和布局，数据模型和图片标签一致：PictureRenderData 。<br>
     * @throws IOException IO异常
     * <p>
     * 列表标签以*开始：{{*var}}
     */
    public static void referencePicTest () throws IOException
    {
        HashMap<String, Object> data = new HashMap<>();
        data.put("img",Pictures.ofLocal("C:\\Users\\Administrator\\Desktop\\表情包\\th.jpg").create());

        myRender(SOURCE_DIR+"ReferencePicture.docx",data,DEST_DIR+"ReferencePicture-output.docx");
    }


    /**
     * 多关系图表测试：条形图（3D条形图）、柱形图（3D柱形图）、面积图（3D面积图）、折线图（3D折线图）、雷达图、散点图
     * 数据模型：ChartMultiSeriesRenderData，推荐使用工厂 Charts 构建图表模型。
     * <p>
     * 1. 在模板中选中要在哪个图表上，然后右键标题右侧区域，选择倒数第二项“编辑替换文字”，在输入区域填写标签来引用该图片，比如{{chart}}<br>
     * <p>
     * <strong>要把绘图区清空，才能绘制出来柱状图</strong>
     * @throws IOException IO异常
     * <p>
     * 列表标签以*开始：{{*var}}
     */
    public static void multiRelationChartTest () throws IOException
    {
        HashMap<String, Object> data = new HashMap<>();

        ChartMultiSeriesRenderData chart = Charts
                .ofMultiSeries("ChartTitle", new String[] { "中文", "English" })
                .addSeries("countries", new Double[] { 15.0, 6.0 })
                .addSeries("speakers", new Double[] { 223.0, 119.0 })
                .create();

        data.put("chart", chart);

        myRender(SOURCE_DIR+"MultiRelationChart.docx",data,DEST_DIR+"MultiRelationChart-output.docx");
    }


    /**
     * 多关系图表测试：条形图（3D条形图）、柱形图（3D柱形图）、面积图（3D面积图）、折线图（3D折线图）、雷达图、散点图
     * 数据模型：ChartMultiSeriesRenderData，推荐使用工厂 Charts 构建图表模型。
     * <p>
     * 1. 在模板中选中要在哪个图表上，然后右键标题右侧区域，选择倒数第二项“编辑替换文字”，在输入区域填写标签来引用该图片，比如{{chart}}<br>
     * <p>
     * <strong>不要清空绘图区!!! 才能绘制出来柱状图</strong>
     * @throws IOException IO异常
     * <p>
     * 列表标签以*开始：{{*var}}
     */
    public static void singleRelationChartTest () throws IOException
    {
        HashMap<String, Object> data = new HashMap<>();

        ChartSingleSeriesRenderData pie = Charts
                .ofSingleSeries("ChartTitle", new String[] { "美国", "中国" })
                .series("countries", new Integer[] { 9826675, 9596961 })
                .create();


        data.put("pieChart", pie);

        myRender(SOURCE_DIR+"SingleRelationChart.docx",data,DEST_DIR+"SingleRelationChart-output.docx");
    }


    /**
     * 组合图表测试：条形图（3D条形图）、柱形图（3D柱形图）的组合
     * 数据模型：ChartMultiSeriesRenderData
     * <p>
     * 1. 在模板中选中要在哪个图表上，然后右键标题右侧区域，选择倒数第二项“编辑替换文字”，在输入区域填写标签来引用该图片，比如{{chart}}<br>
     * <p>
     * <strong>直接插入组合图表， 才能绘制出来柱状图</strong>
     * @throws IOException IO异常
     * <p>
     * 列表标签以*开始：{{*var}}
     */
    public static void combinationChartTest () throws IOException
    {

        HashMap<String, Object> data = new HashMap<>();

        ChartMultiSeriesRenderData comb = Charts
                .ofComboSeries("MyChart", new String[] { "中文", "English" })
                .addBarSeries("countries", new Double[] { 15.0, 6.0 })
                .addBarSeries("speakers", new Double[] { 223.0, 119.0 })
                .addBarSeries("NewBar", new Double[] { 223.0, 119.0 })
                .addLineSeries("youngs", new Double[] { 323.0, 89.0 })
                .addLineSeries("NewLine", new Double[] { 123.0, 59.0 })
                .create();

        data.put("combChart", comb);

        myRender(SOURCE_DIR+"CombinationChart.docx",data,DEST_DIR+"CombinationChart-output.docx");


    }

    /**
     * 通用渲染方法
     * @param sourcePath 模板文件的路径，例如D:\\template.docx
     * @param data 数据
     * @param destinationPath 输出文件的路径，例如D:\\output.docx
     * @throws IOException IO异常
     */
    public static void myRender(String sourcePath,Map<String,Object> data,String destinationPath) throws IOException
    {
        //创建输出流
        OutputStream os = Files.newOutputStream(Paths.get(destinationPath));
        //最终编译渲染并输出
        try(XWPFTemplate xwpfTemplate = XWPFTemplate.compile(sourcePath))
        {
            xwpfTemplate.render(data).writeAndClose(os);
        }
    }

    /**
     * 通用渲染方法
     * @param sourcePath 模板文件的路径，例如D:\\template.docx
     * @param data 数据
     * @param destinationPath 输出文件的路径，例如D:\\output.docx
     * @throws IOException IO异常
     */
    public static void myRender(String sourcePath, Object  data,String destinationPath) throws IOException
    {
        //创建输出流
        OutputStream os = Files.newOutputStream(Paths.get(destinationPath));
        //最终编译渲染并输出
        try(XWPFTemplate xwpfTemplate = XWPFTemplate.compile(sourcePath))
        {
            xwpfTemplate.render(data).writeAndClose(os);
        }
    }


    /**
     * 运行所有类型的渲染的测试
     * @throws IOException IO异常
     */
    public static void test() throws IOException
    {
        simpleRender();
        rowLoopRender();
    }


    public static void main(String[] args) throws IOException
    {
        combinationChartTest();
    }
}
